﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel;
using STARShell.Core.Interfaces;
using System.ServiceModel.Description;
using System.ServiceModel.Channels;

namespace STARShell.Core.DefaultRouter
{
    [ServiceBehavior(InstanceContextMode = InstanceContextMode.Single, ConcurrencyMode=ConcurrencyMode.Multiple, AddressFilterMode = AddressFilterMode.Any, ValidateMustUnderstand = false)]
    public class RouterService : ISTARShellRouterService, IServiceRegistrationService
    {
        object lockObject = new object();
        private IDictionary<int, RegistrationInfo> RegistrationList = new Dictionary<int, RegistrationInfo>();
        private IDictionary<string, int> RoundRobinCount = new Dictionary<string, int>();

        #region IRouterService Members

        public Message ProcessMessage(Message requestMessage)
        {
                Binding binding = null;
                EndpointAddress endpointAddress = null;
                GetServiceEndpoint(requestMessage, out binding, out endpointAddress);
            using (ChannelFactory<ISTARShellRouterService> factory = new ChannelFactory<ISTARShellRouterService>(binding, endpointAddress))
            {
                factory.Endpoint.Behaviors.Add(new MustUnderstandBehavior(false));
                ISTARShellRouterService proxy = factory.CreateChannel();

                using (proxy as IDisposable)
                {
                    // log request message
                    IClientChannel clientChannel = proxy as IClientChannel;
                    Console.WriteLine(String.Format("Request received at {0}, to {1}\r\n\tAction: {2}", DateTime.Now, clientChannel.RemoteAddress.Uri.AbsoluteUri, requestMessage.Headers.Action));


                    // invoke service
                    Message responseMessage = proxy.ProcessMessage(requestMessage);

                    // log response message
                    Console.WriteLine(String.Format("Reply received at {0}\r\n\tAction: {1}", DateTime.Now, responseMessage.Headers.Action));
                    Console.WriteLine();

                    return responseMessage;
                }
            }


        }

        private void GetServiceEndpoint(Message requestMessage, out Binding binding, out EndpointAddress endpointAddress)
        {
            string contractNamespace = requestMessage.Headers.Action.Substring(0, requestMessage.Headers.Action.LastIndexOf("/"));

            // get a list of all registered service entries for the specified contract
            var results = from item in RegistrationList
                          where item.Value.ContractNamespace.Contains(contractNamespace)
                          select item;

            // get last address used
            int index = 0;
            if (!RoundRobinCount.ContainsKey(contractNamespace))
                RoundRobinCount.Add(contractNamespace, 0);
            else
            {
                int lastIndex = RoundRobinCount[contractNamespace];
                if (++lastIndex > results.Count<KeyValuePair<int, RegistrationInfo>>() - 1)
                    lastIndex = 0;

                index = lastIndex;
                RoundRobinCount[contractNamespace] = index;
            }


            RegistrationInfo regInfo = results.ElementAt<KeyValuePair<int, RegistrationInfo>>(index).Value;

            Uri addressUri = new Uri(regInfo.Address);

            binding = ConfigurationUtility.GetRouterBinding(addressUri.Scheme);
            endpointAddress = new EndpointAddress(regInfo.Address);

        }

        #endregion

        #region IRegistrationService Members


        public void Register(RegistrationInfo regInfo)
        {
            if (!RegistrationList.ContainsKey(regInfo.GetHashCode()))
            {
                RegistrationList.Add(regInfo.GetHashCode(), regInfo);
            }
        }

        public void Unregister(RegistrationInfo regInfo)
        {
            if (RegistrationList.ContainsKey(regInfo.GetHashCode()))
            {
                RegistrationList.Remove(regInfo.GetHashCode());
            }
        }

        #endregion
    }
}
